using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace Keb.ToDoList
{
	public partial class ToDoListControl : UserControl
	{
		public event EventHandler<ValueEventArgs<Exception>> ExceptionOccurred;
		public event EventHandler FileClosed;
		public event EventHandler<ValueEventArgs<ToDoList>> ListEdited;
		public event EventHandler<ValueEditEventArgs<ToDoList>> ListEditRequest;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskCopied;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskCopyRequest;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskCut;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskCutRequest;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskDeleted;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskDeleteRequest;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskPasted;
		public event EventHandler TaskPasteRequest;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskAdded;
		public event EventHandler<ValueEventArgs<ToDoTask>> TaskEdited;
		public event EventHandler<ValueEditEventArgs<ToDoTask>> TaskEditRequest;


		private bool _changed;
		private ToDoListFile _currentFile;
		private string _currentFilename = "";
		private Settings _settings;
		private bool _showCompleted;
		private bool _showUncompleted;
		private Dictionary<ToDoTask, TreeNode> _taskNodes = new Dictionary<ToDoTask, TreeNode>();


		public ToDoListControl()
		{
			InitializeComponent();
		}


		virtual public bool Changed
		{
			get { return _changed; }
			protected set { _changed = value; }
		}

		virtual public ToDoListFile CurrentFile
		{
			get { return _currentFile; }
			set
			{
				_currentFile = value;
				RefreshData();
				Changed = false;
			}
		}

		virtual public string CurrentFilename
		{
			get { return _currentFilename; }
			set { _currentFilename = value; }
		}

		virtual public Settings Settings
		{
			get { return _settings; }
			set
			{
				_settings = value;
				UseSettings();
			}
		}

		virtual public bool ShowCompleted
		{
			get { return _showCompleted; }
			set { _showCompleted = value; }
		}

		virtual public bool ShowUncompleted
		{
			get { return _showUncompleted; }
			set { _showUncompleted = value; }
		}

		private Dictionary<ToDoTask, TreeNode> TaskNodes
		{
			get { return _taskNodes; }
			set { _taskNodes = value; }
		}


		virtual public void AddChildTask(ToDoTask task)
		{
			if (tvTasks.SelectedNode == null || (!(tvTasks.SelectedNode.Tag is ToDoTaskContainer)))
			{
				throw new ApplicationException("Cannot add child to this item.");
			}

			ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Tag;
			parent.Items.Add(task);
			tvTasks.SelectedNode.Nodes.Add(FillNode(null, task));
			tvTasks.SelectedNode.Expand();
			Changed = true;
			OnTaskAdded(new ValueEventArgs<ToDoTask>(task));
		}

		virtual public void AddSiblingTask(ToDoTask task)
		{
			if (tvTasks.SelectedNode == null || tvTasks.SelectedNode.Parent == null
					 || (!(tvTasks.SelectedNode.Parent.Tag is ToDoTaskContainer)))
			{
				throw new ApplicationException("Cannot add sibling to this item.");
			}

			ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Parent.Tag;
			ValueEditEventArgs<ToDoTask> taskArgs = new ValueEditEventArgs<ToDoTask>(task);
			parent.Items.Add(task);
			tvTasks.SelectedNode.Parent.Nodes.Add(FillNode(null, task));
			Changed = true;
			OnTaskAdded(new ValueEventArgs<ToDoTask>(task));
		}

		virtual public bool CloseFile()
		{
			bool cancelled = false;
			if (Changed)
			{
				DialogResult result = MessageBox.Show("Save changes?", Text, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
				switch (result)
				{
					case DialogResult.Cancel: cancelled = true; break;
					case DialogResult.No: CurrentFile = null; break;
					case DialogResult.Yes: SaveFile(); CurrentFile = null; break;
				}
			}
			else CurrentFile = null;

			if (!cancelled) OnFileClosed(new EventArgs());
			return cancelled;
		}

		virtual public void CollapseAll()
		{
			tvTasks.CollapseAll();
		}

		virtual public void Copy()
		{
			OnTaskCopyRequest(new ValueEventArgs<ToDoTask>(tvTasks.SelectedNode.Tag as ToDoTask));
		}

		virtual public ToDoTask CreateTask()
		{
			return CurrentFile.ToDoList.CreateTask();
		}

		virtual public void Cut()
		{
			OnTaskCutRequest(new ValueEventArgs<ToDoTask>(tvTasks.SelectedNode.Tag as ToDoTask));
		}

		virtual public void Delete()
		{
			OnTaskDeleteRequest(new ValueEventArgs<ToDoTask>(tvTasks.SelectedNode.Tag as ToDoTask));
		}

		virtual public void Delete(ToDoTask task)
		{
			TreeNode node = TaskNodes[task];
			if (node != null && node.Tag is ToDoTask
					&& node.Parent != null && node.Parent.Tag is ToDoTaskContainer)
			{
				throw new ApplicationException("Cannot delete this item");
			}

			ToDoTaskContainer parent = (ToDoTaskContainer)node.Parent.Tag;
			parent.Items.Remove(task);
			node.Remove();
			Changed = true;
			OnTaskDeleted(new ValueEventArgs<ToDoTask>(node.Tag as ToDoTask));
		}

		virtual public void ExpandAll()
		{
			tvTasks.ExpandAll();
		}

		private TreeNode FillNode(TreeNode node, ToDoList list)
		{
			if (node == null) node = new TreeNode();
			node.Checked = false;
			node.Tag = list;
			node.Text = list.ProjectName;
			if (list.Items.Count > 0 && list.Items.Count != node.Nodes.Count)
			{
				node.Nodes.Add(new TreeNode("Loading..."));
			}
			return node;
		}

		private TreeNode FillNode(TreeNode node, ToDoTask task)
		{
			if (node == null) node = new TreeNode();
			//node.BackColor = task.PriorityColor;
			node.Checked = task.Completed;
			node.ForeColor = task.PriorityColor;
			node.Tag = task;

			string extraText = "";
			string fieldText;
			foreach (DisplayFieldType fieldType in Settings.DisplayFields)
			{
				fieldText = "";
				switch (fieldType)
				{
					case DisplayFieldType.Category: fieldText += task.Category; break;
					case DisplayFieldType.DueDate: fieldText += (task.DueDate.HasValue ? task.DueDate.Value.ToString("yyyy-MM-dd") : ""); break;
					case DisplayFieldType.PercentageDone: fieldText += (task.PercentDone.HasValue ? task.PercentDone.ToString() : ""); break;
					case DisplayFieldType.Person: fieldText += task.Person; break;
					case DisplayFieldType.Priority: fieldText += (task.Priority.HasValue ? task.Priority.ToString() : ""); break;
					case DisplayFieldType.Risk: fieldText += task.Risk; break;
					case DisplayFieldType.Status: fieldText += task.Status; break;
				}
				if (fieldText.Length > 0)
				{
					if (extraText.Length > 0) extraText += ",";
					extraText += fieldText;
				}
			}
			node.Text = task.Title + (extraText.Length > 0 ? node.Text += " {" + extraText + "}" : "");

			if (task.Items.Count > 0 && task.Items.Count != node.Nodes.Count) node.Nodes.Add(new TreeNode("Loading..."));
			return node;
		}

		virtual protected void OnExceptionOccurred(ValueEventArgs<Exception> e)
		{
			if (ExceptionOccurred != null) ExceptionOccurred(this, e);
		}

		virtual protected void OnFileClosed(EventArgs e)
		{
			if (FileClosed != null) FileClosed(this, e);
		}

		virtual protected void OnListEdited(ValueEventArgs<ToDoList> e)
		{
			if (ListEdited != null) ListEdited(this, e);
		}

		virtual protected void OnListEditRequest(ValueEditEventArgs<ToDoList> e)
		{
			if (ListEditRequest != null) ListEditRequest(this, e);
		}

		virtual protected void OnTaskCopied(ValueEventArgs<ToDoTask> e)
		{
			if (TaskCopied != null) TaskCopied(this, e);
		}

		virtual protected void OnTaskCopyRequest(ValueEventArgs<ToDoTask> e)
		{
			if (TaskCopyRequest != null) TaskCopyRequest(this, e);
		}

		virtual protected void OnTaskCut(ValueEventArgs<ToDoTask> e)
		{
			if (TaskCut != null) TaskCut(this, e);
		}

		virtual protected void OnTaskCutRequest(ValueEventArgs<ToDoTask> e)
		{
			if (TaskCutRequest != null) TaskCutRequest(this, e);
		}

		virtual protected void OnTaskDeleted(ValueEventArgs<ToDoTask> e)
		{
			if (TaskDeleted != null) TaskDeleted(this, e);
		}

		virtual protected void OnTaskDeleteRequest(ValueEventArgs<ToDoTask> e)
		{
			if (TaskDeleteRequest != null) TaskDeleteRequest(this, e);
		}

		virtual protected void OnTaskPasted(ValueEventArgs<ToDoTask> e)
		{
			if (TaskPasted != null) TaskPasted(this, e);
		}

		virtual protected void OnTaskPasteRequest(EventArgs e)
		{
			if (TaskPasteRequest != null) TaskPasteRequest(this, e);
		}

		virtual protected void OnTaskAdded(ValueEventArgs<ToDoTask> e)
		{
			if (TaskAdded != null) TaskAdded(this, e);
		}

		virtual protected void OnTaskEdited(ValueEventArgs<ToDoTask> e)
		{
			if (TaskEdited != null) TaskEdited(this, e);
		}

		virtual protected void OnTaskEditRequest(ValueEditEventArgs<ToDoTask> e)
		{
			if (TaskEditRequest != null) TaskEditRequest(this, e);
		}

		virtual public void OpenFile(string filename)
		{
			CurrentFilename = filename;
			CurrentFile = new ToDoListFile(filename);
		}

		virtual public void RefreshData()
		{
			bool enabled = (_currentFile != null);
			txtDetails.Text = "";
			tvTasks.BeginUpdate();
			try
			{
				tvTasks.Nodes.Clear();
				if (enabled)
				{
					TreeNode node = FillNode(null, _currentFile.ToDoList);
					tvTasks.Nodes.Add(node);
					node.Expand();
				}
			}
			finally
			{
				tvTasks.EndUpdate();
			}
			tvTasks.Enabled = enabled;
			btnAdd.Enabled = enabled;
			txtNewTaskTitle.Enabled = enabled;
		}

		virtual public void SaveFile()
		{
			if (CurrentFile == null) throw new NullReferenceException("There is no file to save");
			if (CurrentFilename.Length == 0) SaveFileAs();
			else
			{
				CurrentFile.Save(CurrentFilename);
				Changed = false;
			}
		}

		virtual public void SaveFileAs()
		{
			if (CurrentFile == null) throw new NullReferenceException("There is no file to save");
			if (saveFileDialog1.ShowDialog() == DialogResult.OK)
			{
				CurrentFilename = saveFileDialog1.FileName;
				CurrentFile.Save(CurrentFilename);
				Changed = false;
			}
		}

		private void UseSettings()
		{
			txtComments.Visible = Settings.ShowComments;
			txtDetails.Visible = Settings.ShowDetails;
		}
		

		private void btnAdd_Click(object sender, EventArgs e)
		{
			try
			{
				ToDoTask task = CreateTask();
				task.Title = txtNewTaskTitle.Text;
				AddChildTask(task);
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskCopy_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null)
				{
					OnTaskCopyRequest(new ValueEventArgs<ToDoTask>(tvTasks.SelectedNode.Tag as ToDoTask));
				}
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskCut_Click(object sender, EventArgs e)
		{
			try
			{
				OnTaskCutRequest(new ValueEventArgs<ToDoTask>(tvTasks.SelectedNode.Tag as ToDoTask));
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskDelete_Click(object sender, EventArgs e)
		{
			try
			{
				Delete();
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskEdit_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null)
				{
					if (tvTasks.SelectedNode.Tag is ToDoList)
					{
						ToDoList list = (ToDoList)tvTasks.SelectedNode.Tag;
						ValueEditEventArgs<ToDoList> listArgs = new ValueEditEventArgs<ToDoList>(list);
						OnListEditRequest(listArgs);
						if (listArgs.Changed)
						{
							Changed = true;
							FillNode(tvTasks.SelectedNode, list);
							txtDetails.Text = list.ToString();
							OnListEdited(new ValueEventArgs<ToDoList>(list));
						}
					}
					else if (tvTasks.SelectedNode.Tag is ToDoTask)
					{
						ToDoTask task = (ToDoTask)tvTasks.SelectedNode.Tag;
						ValueEditEventArgs<ToDoTask> taskArgs = new ValueEditEventArgs<ToDoTask>(task);
						OnTaskEditRequest(taskArgs);
						if (taskArgs.Changed)
						{
							FillNode(tvTasks.SelectedNode, task);
							txtComments.Text = task.Comments;
							txtDetails.Text = task.ToString();
							Changed = true;
							OnTaskEdited(new ValueEventArgs<ToDoTask>(task));
						}
					}
				}
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskNewChild_Click(object sender, EventArgs e)
		{
			try
			{
				ToDoTask task = CreateTask();
				ValueEditEventArgs<ToDoTask> args = new ValueEditEventArgs<ToDoTask>(task);
				OnTaskEditRequest(args);
				if (args.Changed) AddChildTask(task);
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskNewSibling_Click(object sender, EventArgs e)
		{
			try
			{
				ToDoTask task = CreateTask();
				ValueEditEventArgs<ToDoTask> args = new ValueEditEventArgs<ToDoTask>(task);
				OnTaskEditRequest(args);
				if (args.Changed) AddSiblingTask(task);
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void cmiTaskPaste_Click(object sender, EventArgs e)
		{
			try
			{
				OnTaskPasteRequest(new EventArgs());
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void tvTasks_AfterCheck(object sender, TreeViewEventArgs e)
		{
			try
			{

				if (e.Node != null)
				{
					if (e.Node.Tag is ToDoList)
					{
						if (e.Node.Checked) e.Node.Checked = false;
					}
					else if (e.Node.Tag is ToDoTask)
					{
						ToDoTask task = (ToDoTask)e.Node.Tag;
						if (e.Node.Checked) task.SetComplete();
						else task.SetUncomplete(Settings.UncompletePercentage);
						Changed = true;
					}
				}
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void tvTasks_AfterExpand(object sender, TreeViewEventArgs e)
		{
			try
			{
				if (e.Node.Tag is ToDoTaskContainer)
				{
					tvTasks.BeginUpdate();
					try
					{
						((ToDoTaskContainer)e.Node.Tag).Sort(Settings.SortType);
						e.Node.Nodes.Clear();
						foreach (ToDoTask task in ((ToDoTaskContainer)e.Node.Tag).Items)
						{
							if ((ShowCompleted && task.ContainsCompleted)
								|| (ShowUncompleted && task.ContainsIncompleted)) e.Node.Nodes.Add(FillNode(null, task));
						}
					}
					finally
					{
						tvTasks.EndUpdate();
					}
				}
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}

		private void tvTasks_AfterSelect(object sender, TreeViewEventArgs e)
		{
			try
			{
				if (e.Node.Tag != null) txtDetails.Text = e.Node.Tag.ToString();
				if (e.Node.Tag is ToDoTask) txtComments.Text = ((ToDoTask)e.Node.Tag).Comments;
			}
			catch (Exception ex)
			{
				OnExceptionOccurred(new ValueEventArgs<Exception>(ex));
			}
		}
	}
}
